import java.util.ArrayList;
import java.util.Date;
import java.util.Timer;

import advs.ServicePublisherAdvertisement;

import utils.JXTAMessageUtils;
import utils.ReflectionHelper;
import utils.SerializeHelper;
import utils.ApplicationState;

import net.jxta.document.Advertisement;
import net.jxta.document.Document;
import net.jxta.document.MimeMediaType;
import net.jxta.endpoint.Message;
import net.jxta.endpoint.MessageElement;
import net.jxta.endpoint.StringMessageElement;
import net.jxta.endpoint.Message.ElementIterator;
import net.jxta.pipe.PipeMsgEvent;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.util.JxtaBiDiPipe;
import net.jxta.util.JxtaServerPipe;

public class ThreadPublisher extends Thread
{
	public int TimeToCheckWhenWorking = 1000;
	private ArrayList<Advertisement> advs = new ArrayList<Advertisement>();
	private int lifetime;
	private int expiration;
	private String peerToPublishTo;
	private int repeatTime;
	private JxtaApplication application;
	private boolean printAdvOnPublish = false;

	private boolean started = false;

	JxtaBiDiPipe bidiPipe;

	public PipeAdvertisement getPipeAdvertisement()
	{
		PipeAdvertisement pipeAdv = null;
		int nrOccurences = 0;
		for (int i = 0; i < advs.size(); i++)
		{
			if (advs.get(i).getAdvType() == PipeAdvertisement
					.getAdvertisementType())
			{
				pipeAdv = (PipeAdvertisement) advs.get(i);
				nrOccurences++;
			}
		}
		if (nrOccurences > 1)
		{
			application.Log("ERROR:multiple pipe advs on thread");
		}
		return pipeAdv;
	}

	public JxtaBiDiPipe getBidiPipe()
	{
		return bidiPipe;
	}

	public void setBidiPipe(JxtaBiDiPipe bidiPipe)
	{
		this.bidiPipe = bidiPipe;
	}

	public boolean getStarted()
	{
		return started;
	}

	public void setStarted(boolean started)
	{
		this.started = started;
	}

	ThreadPublisher(JxtaApplication application)
	{
		this.application = application;
	}

	@Override
	public void run()
	{
		started = true;
		while (!this.isInterrupted())
		{
			System.out.println("Contiguous publishing");
			for (int i = 0; i < advs.size(); i++)
			{
				try
				{
					application.getDiscoveryService().publish(advs.get(i),
							lifetime, expiration);
				}
				catch (Exception e)
				{
					application.Log("ERROR publishing local adv");
					e.printStackTrace();
				}
				application.getDiscoveryService().remotePublish(
						peerToPublishTo, advs.get(i), expiration);
			}

			application.Log(getName() + " thread published its adv");
			if (printAdvOnPublish)
			{
				for (int i = 0; i < advs.size(); i++)
				{
					Document doc = advs.get(i).getDocument(
							MimeMediaType.TEXTUTF8);
					try
					{
						doc.sendToStream(System.out);
					}
					catch (Exception exc)
					{
						exc.printStackTrace();
					}
				}
			}

			PipeAdvertisement pipeAdv = getPipeAdvertisement();
			if (pipeAdv != null)
			{
				int timeToWait = repeatTime;
				while (timeToWait > 0)
				{
					long before = System.currentTimeMillis();
					if (application.getApplicationState() == ApplicationState.Available)
					{
						try
						{
							application.setServerPipe(new JxtaServerPipe(
									application.getGroup(), pipeAdv));
							setBidiPipe(new JxtaBiDiPipe());

							application.getServerPipe().setPipeTimeout(
									timeToWait);

							application
									.Log("Accepting a connection on the pipe:"
											+ pipeAdv.getPipeID());
							setBidiPipe(application.getServerPipe().accept());
							long after = System.currentTimeMillis();
							long durationToAccept = after - before;
							application.Log("It took " + durationToAccept
									+ " milliseconds to accept a connection");

							timeToWait -= durationToAccept;
							if (getBidiPipe() == null)
							{
								application.Log("connection failed");
							}
							else
							{
								getBidiPipe().setMessageListener(application);
								int additionalTime = 1000;
								timeToWait -= additionalTime;
								Thread.sleep(additionalTime);
								application.SendInfoAboutTheState(
										getBidiPipe(), pipeAdv.getPipeID()
												.toString());
								application
										.setApplicationState(ApplicationState.Working);
								application.Log("connection succesfull");
							}
						}
						catch (Exception exc)
						{
							// application.Log("ERROR accepting a client:"+
							// exc.getMessage());
						}
					}
					else
					{
						// application.Log("Waiting fast for state changing:"+
						// timeToWait);
						try
						{
							timeToWait -= TimeToCheckWhenWorking;
							Thread.sleep(TimeToCheckWhenWorking);
						}
						catch (InterruptedException e)
						{
							this.interrupt();
						}
					}
				}
			}
			else
			{
				// application.Log("this thread contains no pipe advs");
				try
				{
					// application.Log("waiting " + expiration+
					// " milliseconds before publishing advs again");
					Thread.sleep(expiration);
				}
				catch (InterruptedException e)
				{
					this.interrupt();
				}
			}
		}
		System.out.println("Secondary thread ending");
	}

	public ArrayList<Advertisement> getAdvs()
	{
		return advs;
	}

	public void setAdv(ArrayList<Advertisement> advs)
	{
		this.advs = advs;
	}

	public void addAdv(Advertisement adv)
	{
		advs.add(adv);
	}

	public int getLifetime()
	{
		return lifetime;
	}

	public void setLifetime(int lifetime)
	{
		this.lifetime = lifetime;
	}

	public int getExpiration()
	{
		return expiration;
	}

	public void setExpiration(int expiration)
	{
		this.expiration = expiration;
	}

	public String getPeerToPublishTo()
	{
		return peerToPublishTo;
	}

	public void setPeerToPublishTo(String peerToPublishTo)
	{
		this.peerToPublishTo = peerToPublishTo;
	}

	public int getRepeatTime()
	{
		return repeatTime;
	}

	public void setRepeatTime(int repeatTime)
	{
		this.repeatTime = repeatTime;
	}

	public JxtaApplication getApplication()
	{
		return application;
	}

	public void setApplication(JxtaApplication application)
	{
		this.application = application;
	}

	public boolean isPrintAdvOnPublish()
	{
		return printAdvOnPublish;
	}

	public void setPrintAdvOnPublish(boolean printAdvOnPublish)
	{
		this.printAdvOnPublish = printAdvOnPublish;
	}

}
